
use ethereum_types::{
    H256,
    Address
};
use crate::{
    SimpleList,
    WhiteUserSet
};
use common_types::{
    engines::machine::Call,
    ids::BlockId
};
use ethabi::FunctionOutputDecoder;
use parking_lot::{Mutex, RwLock};
use std::sync::{Arc, Weak};
use client_traits::{BlockChainClient, EngineClient, TransactionRequest};
use ethabi_contract::use_contract;
use log::{debug, info, trace, warn};
use client_traits::StateOrBlock::Block;
use_contract!(whiteusers_set, "res/whiteusers_set.json");
use_contract!(validator_set,"res/validator_set.json");
/// 白名单智能合约
/// finish 除了增加和删除之外的都完成了
pub struct WhiteUserContract {
    contract_address: Address,
    client: RwLock<Option<Weak<dyn EngineClient>>>, // TODO [keorn]: remove
}

impl WhiteUserContract{
    pub fn new(contract_address: Address) -> Self {
        WhiteUserContract{
            contract_address,
            client: RwLock::new(None),
        }
    }

    fn get_list(&self, caller: &Call) -> Option<SimpleList> {
        let contract_address = self.contract_address;
        // let contract_address1 = self.contract_address;
        let (data, decoder) = whiteusers_set::functions::get_white_users::call();
        /// 实际上调用时client.call_contract
        // println!("{:?}",data);
        let value = caller(contract_address, data).and_then(|x| decoder.decode(&x.0).map_err(|e| e.to_string()));

        // let (data1,decoder1) = whiteusers_set::functions::whiteusers::call(1);
        // let value1 = caller(contract_address1,data1).and_then(|x| decoder1.decode(&x.0).map_err(|e| e.to_string()));
        // match value1 {
        //     Ok(new) => {
        //         println!("{:?}",new)
        //     },
        //     Err(s) => {
        //         println!("{}",s)
        //     }
        // }
        match value {
            Ok(new) => {
                debug!(target: "engine", "Got whiteuser set from contract: {:?}", new);
                Some(SimpleList::new(new))
            },
            Err(s) => {
                warn!("use contract error:{}",s);
                debug!(target: "engine", "Could not get whiteuser set from contract: {}", s);
                None
            },
        }
    }
}
impl WhiteUserSet for WhiteUserContract {
    fn count(&self) -> usize {
        let default = self.defalult_caller();
        let list_from_contract = self.get_list(&*default);
        match list_from_contract{
            Some(T) => {
                T.whiteusers.len()
            },
            None => {
                0
            }
        }

    }
    fn contains_with_caller(&self, address: &Address, caller: &Call) -> bool {
        let list_from_contract = self.get_list(caller);
        match list_from_contract {
            Some(T) => {
                println!("{:?}",T.whiteusers);
                T.whiteusers.contains(address)
            },
            None => {
                false
            }
        }
    }

    /// 默认使用最新的区块id----这里必须要使用区块id我也不知道是个什么意思
    /// 这里就先不使用blockid传参的方式了
    fn defalult_caller(&self) -> Box<Call> {
        let client = self.client.read().clone();
        /// 直接转型之后获得blockid
        match client {
            Some(ref T) => {
                println!("has a client")
            },
            None => {
                println!("no client")
            }
        }
        Box::new(move |addr, data| client.as_ref()
            .and_then(Weak::upgrade)
            .ok_or_else(|| "No client!".into())
            .and_then(|c| {
                match c.as_full_client() {
                    Some(c) => {
                        let header = c.get_block_parent_hash();
                        let cpy = header.hash();
                        // println!("{:?}",header.parent_hash());
                        // println!("{:}",header.hash());
                        c.call_contract(BlockId::Hash(cpy),addr, data)
                    },
                    None => Err("No full client!".into()),
                }
            })
            .map(|out| (out, Vec::new()))) // generate no proofs in general
    }

    fn register_client(&self, client: Weak<dyn EngineClient>) {
        trace!(target: "engine", "Setting up contract caller.");
        *self.client.write() = Some(client);
    }
}
#[cfg(test)]
mod tests {
    use std::sync::Arc;

    use accounts::AccountProvider;
    use call_contract::CallContract;
    use common_types::{header::Header, ids::BlockId};
    use client_traits::{BlockChainClient, ChainInfo, BlockInfo, TransactionRequest};
    use ethabi::FunctionOutputDecoder;
    use ethcore::{
        miner::{self, MinerService},
        test_helpers::generate_dummy_client_with_spec,
    };
    use ethereum_types::{H520, Address};
    use keccak_hash::keccak;
    use parity_bytes::ToPretty;
    use rlp::encode;
    use rustc_hex::FromHex;
    use spec;


    use super::WhiteUserContract;
    use super::super::WhiteUserSet;
    use ethabi_contract::use_contract;
    use_contract!(whiteusers_set, "res/whiteusers_set.json");


    #[test]
    fn fetches_validators() {
        /// 单元测试一下，获得智能合约中的白名单数据
        let client = generate_dummy_client_with_spec(spec::new_whiteuser_contract);
        let addr: Address = "0000000000000000000000000000000000000005".parse().unwrap();
        let vc = Arc::new(WhiteUserContract::new(addr));
        vc.register_client(Arc::downgrade(&client) as _);
        let last_hash = client.best_block_header().hash();
        println!("last_hash:{:}",last_hash);
        // let white_user = &"5550cfe3244bda8d2ed3c93248e8145445096ec5".parse::<Address>().unwrap();
        println!("{}",vc.count());
        // println!("{}",client.white_user_count());

    }
    // #[test]
    // // 测试设置白名单
    // fn set_whiteusers() {
    //     let client = generate_dummy_client_with_spec(spec::new_whiteuser_contract);
    //     let addr: Address = "0000000000000000000000000000000000000005".parse().unwrap();
    //     let vc = Arc::new(WhiteUserContract::new(addr));
    //     vc.register_client(Arc::downgrade(&client) as _);
    // }

}
